package com.dingnate.framework.springmvn;

import com.dingnate.framework.springmvn.annotation.GPAutowrited;
import com.dingnate.framework.springmvn.annotation.GPController;
import com.dingnate.framework.springmvn.annotation.GPService;
import com.dingnate.framework.springmvn.scaner.DirectoryScanner;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

public class GPApplicationContext {
    Map<String, Object> instanceMapping = new ConcurrentHashMap<>();
    private List<String> classCache;

    public Properties getConfig() {
        return config;
    }

    Properties config = new Properties();

    public Object getBean(String name) {
        return instanceMapping.get(name);
    }

    public GPApplicationContext(String localtion) {
        try {
            //定位
            InputStream is = this.getClass().getClassLoader().getResourceAsStream(localtion);

            //加载
            config.load(is);
            //注册,把所有的class找出来存着
            doRegister(config.getProperty("scanPackage"));
            //实例化，实例化需要的对象(@Controller,@Service等)，只要循环class了
            doCreateBean();
            //注入
            populate();
        } catch (IOException e) {
            e.printStackTrace();
        }

        System.out.println("IOC 容器初始化完毕");
    }

    private void populate() {
        if (instanceMapping.isEmpty()) {
            return;
        }

        for (Object instance : instanceMapping.values()) {
            Field[] fields = instance.getClass().getDeclaredFields();
            for (Field field : fields) {
                if (!field.isAnnotationPresent(GPAutowrited.class)) {
                    continue;
                }
                GPAutowrited ann = field.getAnnotation(GPAutowrited.class);
                String id = ann.value().trim();
                if ("".equals(id)) {
                    id = field.getType().getName();
                }
                field.setAccessible(true);

                Object o = instanceMapping.get(id);
                if (o != null) {
                    try {
                        field.set(instance, o);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }

    }

    private void doCreateBean() {
        try {
            for (String className : classCache) {
                Class<?> clazz = Class.forName(className);
                if (clazz.isAnnotationPresent(GPController.class)) {
                    String id = lowerFirstChar(clazz.getSimpleName());
                    instanceMapping.put(id, clazz.newInstance());
                } else if (clazz.isAnnotationPresent(GPService.class)) {
                    GPService ann = clazz.getAnnotation(GPService.class);
                    Object instance = clazz.newInstance();
                    String id = ann.value().trim();
                    if (!"".equals(id)) {
                        instanceMapping.put(id, instance);
                        continue;
                    }

                    // 如果这个类实现了接口，就用接口的类型作为id
                    for (Class<?> i : clazz.getInterfaces()) {
                        instanceMapping.put(i.getName(), instance);
                    }
                } else {
                    continue;
                }

            }
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    private String lowerFirstChar(String name) {
        if (name.charAt(0) <= 'Z' && name.charAt(0) >= 'A') {
            char[] chars = name.toCharArray();
            chars[0] += 32;
            return new String(chars);
        }
        return name;
    }

    private void doRegister(String scanPackage) {
        URL url = getClass().getClassLoader().getResource(scanPackage.replace('.', '/'));
        if (url == null || !url.getProtocol().equals("file")) {
            return;
        }
        URL rootUrl = getClass().getClassLoader().getResource("");
        String rootFile = rootUrl.getFile();
        classCache = new DirectoryScanner(url.getFile()).scan((o) -> {
            File file = (File) o;
            if (file.isDirectory()) {
            } else if (file.isFile()) {
                if (file.getName().endsWith(".class")) {
                    String path = file.getAbsolutePath();
                    return path.substring(rootFile.length(), path.length() - ".class".length()).replace('/', '.');
                } else {
                    return null;
                }
            }
            return file.getAbsolutePath();
        });
    }

    public Map<String, Object> getAll() {
        return instanceMapping;
    }
}